from abc import ABCMeta, abstractclassmethod
import time

#观察者基类，或叫做subscriber
class Observer(metaclass=ABCMeta):
    @abstractclassmethod
    # 也可以叫做response()
    # 在基类里必须传入被观察者基类（subject、subscriber）的引用
    def update(self, observable, object):
        pass

#被观察者基类，或叫做publisher、subject
class Observable:
    def __init__(self):
        self._observers = []

    def addObserver(self, observer):
        self._observers.append(observer)
    
    def removeObserver(self, observer):
        self._observers.remove(observer)
    
    def notifyObservers(self, object=0):
        for o in self._observers:
            o.update(self, object)

class WaterHeater(Observable):
    def __init__(self):
        super().__init__()
        self._temperature = 25
    
    def getTemperature(self):
        return self._temperature
    
    def setTemperature(self, temperature):
        self._temperature =temperature
        print("当前温度是："+str(self._temperature)+" 度")
        self.notifyObservers()


# isinstance的用途在于，用一个方法做到识别不同的目标、发布者，并做出不同的处理。
class WashingMode(Observer):
    def update(self, observable, object):
        if isinstance(observable, WaterHeater) and observable.getTemperature()>=50 and observable.getTemperature()<=70:
            print("水已烧好！温度正好，可以用来洗澡了。")
    
class DrinkingMode(Observer):
    def update(self, observable, object):
        if isinstance(observable, WaterHeater) and observable.getTemperature()>=100:
            print("水已烧开！可以饮用了。")

# # 测试代码
# if __name__ == "__main__":
#     heater = WaterHeater()
#     washingObserver = WashingMode()
#     drinkingObserver = DrinkingMode()
#     heater.addObserver(washingObserver)
#     heater.addObserver(drinkingObserver)
#     heater.setTemperature(40)
#     heater.setTemperature(50)
#     heater.setTemperature(60)
#     heater.setTemperature(70)
#     heater.setTemperature(80)
#     heater.setTemperature(90)
#     heater.setTemperature(100)



# ########################################
# 账户登录异常状态监测实例
# ########################################
class Account(Observable):
    def  __init__(self):
        super().__init__()
        self._latestIp = {}
        self._latestRegion = {}
    
    def login(self, name, ip, time):
        region = self._getRegion(ip)
        if self._isLongDistance(name, region):
            self.notifyObservers({"name":name, "ip":ip, "region":region, "time":time})
        self._latestRegion[name] = region
        self._latestIp[name] = ip
    
    def _getRegion(self, ip):
        # 
        ipRegions = {
            "101.47.18.9": "浙江省杭州市",
            "67.218.147.69": "美国洛杉矶"
        }
        region = ipRegions.get(ip)
        return "" if region is None else region
    
    def _isLongDistance(self, name, region):
        latestRegion = self._latestRegion.get(name)
        return latestRegion is not None and latestRegion != region
    
class SmsSender(Observer):
    # 
    def update(self, observable, object):
        print("[短信发送]"+object["name"]+"您好！检测到您的账户可能登录异常。最近一次登录信息:\n"
            +"登录地区："+object["region"]+" 登录ip:"+object["ip"]+" 登录时间: "+time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(object["time"])))

class EmailSender(Observer):
    # 
    def update(self, observable, object):
        print("[邮件发送]"+object["name"]+"您好！检测到您的账户可能登录异常。最近一次登录信息:\n"
            +"登录地区："+object["region"]+" 登录ip:"+object["ip"]+" 登录时间: "+time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(object["time"])))


# 测试代码
if __name__ == "__main__":
    account = Account()
    account.addObserver(SmsSender())
    account.addObserver(EmailSender())
    account.login("Tony", "101.47.18.9", time.time())
    account.login("Tony", "67.218.147.69", time.time())
